/*
 * Copyrignt (c) xuzeshui.com. All Rights Reserved.
 * Author: Zeshui Xu<i@xuzeshui.com>
 * Created Time: 2015-09-29 21:12:16
 * Last Modified: 2017-03-03 14:05:03
 * File Name: login/thirdlogin.go
 * Description:
 */
package login

import (
	"time"

	"passport/account"
	"passport/captcha"
	"passport/errorcode"
	"passport/invitecode"
	"passport/models"
	"passport/user"

	"doubimeizhi.com/utility"

	log "github.com/cihub/seelog"
)

/*
	第三方平台账号登录关联绑定
*/
func (p *LoginService) ThirdAccountLoginBindMobile(req *ThirdAccountLoginReq) (uint, string, *LoginResp) {
	if nil == req {
		log.Warnf("params for ThirdAccountLoginReq is NULL")
		return errorcode.EC_GL_INVALID_JSON_BODY, errorcode.Msg(errorcode.EC_GL_INVALID_JSON_BODY), nil
	}

	if !account.IsValidAccountType(req.ExtFrom) {
		log.Warnf("NOT support account type for %s", req.ExtFrom)
		return errorcode.EC_GL_INVALID_ACCOUNT_TYPE, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_TYPE), nil
	}

	if req.ExtId == "" {
		log.Warnf("empty extid for %s, regip: %s, regplat: %d", req.ExtFrom, req.RegIp, req.RegPlat)
		return errorcode.EC_GL_INVALID_ACCOUNT_ID, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_ID), nil
	}

	var aiByOpenId *account.AccountInfo
	if ai, err := account.GetAccountService().GetAllAccountInfo(req.ExtFrom, req.ExtId); err == nil {
		aiByOpenId = ai
	} else {
		log.Warnf("GetAllAccountInfo by openid error: %s", err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, "系统内部错误", nil
	}

	//账号禁止登录
	if aiByOpenId != nil && aiByOpenId.Active == account.AI_ACTIVE_NOLOGIN {
		log.Warnf("account is not active for open_id.  extFrom: %s, extId: %s, union_id: %s", req.ExtFrom, req.ExtId, req.UnionId)
		return errorcode.EC_LOGIN_ACCOUNT_FREEZE, "account is freezed", nil
	}

	//判断用户是否绑定过手机号
	needBindMobile, code, msg, ui := p.ThirdAccountNeedBindMobile(aiByOpenId)
	log.Debugf("needBindMobile: %s, code: %d, msg: %s", needBindMobile, code, msg)
	if !needBindMobile {
		if code == errorcode.EC_GL_SUCCESS {
			if code, msg, _ := p.TryThirdAccountBind2Userid(ui.UserId, req); code == errorcode.EC_GL_SUCCESS {
				p.changeThirdAccountInfo(aiByOpenId, req)
				p.changeUserInfo(ui, req)
				return p.login(ui.UserId, req)
			} else {
				return code, msg, nil
			}
		} else {
			return code, msg, nil
		}
	}

	if req.Mobile == "" { //请求中没带上手机号
		log.Debugf("req.Mobile is empty, code: %d, msg: %s", code, msg)
		return code, msg, nil
	}

	captchaCheckReq := &captcha.ValidateCaptchaReq{
		Mobile:  req.Mobile,
		Bid:     captcha.CAPTCHA_MOBILE_BIND_THIRD_ACCOUNT,
		Captcha: req.Captcha,
	}
	if code, msg := captcha.GetCaptchaService().ValidateCaptchaForMobile(captchaCheckReq); code != errorcode.EC_GL_SUCCESS {
		return code, msg, nil
	}
	//删除验证码
	captcha.GetCaptchaService().DeleteCaptchaInfoItem(req.Mobile, captcha.CAPTCHA_MOBILE_BIND_THIRD_ACCOUNT)

	if code, msg, userid := p.ThirdAccountBindMobile(aiByOpenId, req); code != errorcode.EC_GL_SUCCESS {
		return code, msg, nil
	} else {
		p.changeThirdAccountInfo(aiByOpenId, req)
		return p.login(userid, req)
	}
}

//判断第三方账号是否已绑定到手机上
//第一返回参数:true表示需要绑定,false表示不需要绑定,具体原因看第二,三参数
//第二返回参数:EC_GL_SUCCESS表示虽然不需要绑定,且取到了正确的用户信息
func (p *LoginService) ThirdAccountNeedBindMobile(aiByOpenId *account.AccountInfo) (bool, uint, string, *user.UserInfo) {
	var userid string
	if aiByOpenId != nil {
		userid = aiByOpenId.Userid
	}
	if userid == "" { //说明是首次在账号系统出现
		return true, errorcode.EC_LOGION_THIRD_ACCOUNT_NEED_BIND_MOBILE, "请绑定手机号", nil
	}
	ui, err := user.GetUserService().GetAllUserInfo(userid)
	if err != nil || ui == nil { //系统错误
		return false, errorcode.EC_GL_INTERNAL_ERROR, "系统内部错误", nil
	}
	if ui.Mobile == "" {
		log.Debugf("need bind, but mobile is empty,userid: %s", userid)
		return true, errorcode.EC_LOGION_THIRD_ACCOUNT_NEED_BIND_MOBILE, "请绑定手机号", ui
	}
	return false, errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), ui
}

//第三方账号往手机号上绑定
func (p *LoginService) ThirdAccountBindMobile(aiByOpenId *account.AccountInfo, req *ThirdAccountLoginReq) (uint, string, string) {
	var aiByMobile *account.AccountInfo
	if ai, err := account.GetAccountService().GetAllAccountInfo(account.ACCOUNT_TYPE_MOBILE, req.Mobile); err == nil {
		aiByMobile = ai
	} else {
		log.Warnf("ThirdAccountBindMobile %s but GetAllAccountInfo error: %s", req.Mobile, err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, "系统内部错误", ""
	}
	var bindUserid string
	if aiByMobile != nil {
		if aiByOpenId != nil && aiByOpenId.Userid != aiByMobile.Userid {
			return errorcode.EC_THIRDACCOUNT_BIND_MOBILE_ABORT, "该账号无法进行绑定操作", ""
		}
		bindUserid = aiByMobile.Userid
	} else { //无手机账号,需要创建
		if code, msg, userid := p.createMobileUser(req); code != errorcode.EC_GL_SUCCESS {
			return code, msg, ""
		} else {
			bindUserid = userid
			account.GetAccountBindService().RecordUserAccountBind(userid, account.ACCOUNT_TYPE_MOBILE, req.Mobile)
		}
	}
	return p.TryThirdAccountBind2Userid(bindUserid, req)
}

/*
	已知userid,尝试将未绑定的第三方账号信息绑定到该userid上
*/
func (p *LoginService) TryThirdAccountBind2Userid(bindUserid string, req *ThirdAccountLoginReq) (uint, string, string) {
	userBinded, userBindedErr := account.GetAccountBindService().GetBindedAccountForUserid(bindUserid)
	if userBindedErr != nil {
		log.Warnf("GetBindedAccountForUserid error: %s, userid: %s", userBindedErr.Error(), bindUserid)
		return errorcode.EC_GL_INTERNAL_ERROR, "系统内部错误", ""
	}

	abi := &account.AccountBindInfoReq{}
	//openid绑定到bindUserid
	if openid, ok := userBinded[req.ExtFrom]; !ok {
		abi.ExtFrom = req.ExtFrom
		abi.ExtId = req.ExtId
		abi.NickName = req.NickName
		account.GetAccountBindService().AccountBind(bindUserid, true, abi)
	} else if openid != req.ExtId { //同一用户,相同的第三方账号,不同的openid
		log.Errorf("bind failed. mobile: %s, bindUserid: %s, ext_from: %s, binedOpenid: %s, BUT openid: %s", req.Mobile, bindUserid, req.ExtFrom, openid, req.ExtId)
		return errorcode.EC_THIRDACCOUNT_BIND_MOBILE_ABORT, "该账号无法进行绑定操作", ""
	}

	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), bindUserid
}

//通过手机号创建新用户
//当成功的时候,第三个参数返回新创建的userid
func (p *LoginService) createMobileUser(req *ThirdAccountLoginReq) (uint, string, string) {
	dispIC := invitecode.GetInviteCodeService().ObtainNewInviteCode() //分配一个邀请码

	//userid用户信息生成
	userid := utility.GenerateUUIDToken()
	uinfoChanger := user.NewUserInfoChanger(userid)
	uinfoChanger.SetUserid(userid)
	uinfoChanger.SetPassword(req.Mobile[5:]) //初始密码手机号后6位
	uinfoChanger.SetMobile(req.Mobile)
	uinfoChanger.SetNickName(req.NickName)
	uinfoChanger.SetAvatar(req.Avatar)
	uinfoChanger.SetIsThirdAccount(account.AI_SELF_ACCOUNT) //非第三方账号
	uinfoChanger.SetRegIp(req.RegIp)
	uinfoChanger.SetRegPlat(req.RegPlat)
	uinfoChanger.SetInsertTime(time.Now().Format("2006-01-02 15:04:05"))
	uinfoChanger.SetInviteCode(dispIC)
	if err := uinfoChanger.Save(); err != nil {
		log.Warnf("uinfoChanger save error, msg: %s", err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), ""
	}

	//手机登陆账号生成
	aichanger := account.NewAccountInfoChanger(req.Mobile, account.ACCOUNT_TYPE_MOBILE)
	aichanger.SetAccount(req.Mobile)
	aichanger.SetAccountType(account.ACCOUNT_TYPE_MOBILE)
	aichanger.SetUserid(userid)
	aichanger.SetActive(account.AI_ACTIVE_ALLOW_LOGIN)
	aichanger.SetThirdAccount(account.AI_SELF_ACCOUNT)
	aichanger.SetBindStatus(account.AI_BIND_STATUS)
	aichanger.SetInsertTime(time.Now().Format("2006-01-02 15:04:05"))
	if err := aichanger.Save(); err != nil {
		log.Errorf("aichanger save error, msg: %s", err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), ""
	}

	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), userid
}

//用户userid登录系统产生token
func (p *LoginService) login(userid string, req *ThirdAccountLoginReq) (uint, string, *LoginResp) {
	accessToken, ok := account.GetAccessTokenService().GenerateAndRecordAccessToken(userid, req.AKId)
	if !ok {
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}
	log.Infof("login extFrom: %s, extId: %s, union_id: %s with accessToekn: %s, userid: %s", req.ExtFrom, req.ExtId, req.UnionId, accessToken, userid)
	resp := &LoginResp{}
	resp.AccessToken = accessToken
	resp.Userid = userid
	resp.ExpireSecond = models.GetAccessTokenExpireSecond()
	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), resp
}

/*
	尝试更新第三方账号的昵称,头像等信息
*/
func (p *LoginService) changeThirdAccountInfo(aiByOpenId *account.AccountInfo, req *ThirdAccountLoginReq) {
	if aiByOpenId != nil && req.NickName != "" && len(req.NickName) < 32 && req.NickName != aiByOpenId.NickName {
		aichanger := account.NewAccountInfoChanger(aiByOpenId.Account, aiByOpenId.AccountType)
		aichanger.SetNickName(req.NickName)
		aichanger.Save()
		log.Infof("change account nickname from %s to %s. extFrom: %s, extId: %s, uinionId: %s", aiByOpenId.NickName, req.NickName, req.ExtFrom, req.ExtId, req.UnionId)
	}
}

func (p *LoginService) changeUserInfo(ui *user.UserInfo, req *ThirdAccountLoginReq) {
	if ui == nil {
		return
	}
	uinfoChanger := user.NewUserInfoChanger(ui.UserId)
	uinfoChanger.SetUserid(ui.UserId)
	if req.NickName != "" && ui.NickName == "" {
		uinfoChanger.SetNickName(req.NickName)
	}
	if req.Avatar != "" && ui.Avatar == "" {
		uinfoChanger.SetAvatar(req.Avatar)
	}
	if err := uinfoChanger.Save(); err != nil {
		log.Warnf("uinfoChanger save error, msg: %s", err.Error())
	}

}
